﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Data;
using System.ComponentModel;
using Caliburn.Micro.BikeWizard.Validation;

namespace Caliburn.Micro.BikeWizard.Utils
{
    /// <summary>
    /// Basic implementation of a wizard that can move back and forth between viewmodels.
    /// Uses a Conductor<T>.Collection.OneActive instance internally to keep track of the items and
    /// properly activate and deactivate them
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class Wizard<T> : Screen where T : class
    {
        #region Events
        public event EventHandler<ActivationProcessedEventArgs> ActivationProcessed = delegate { };
        #endregion

        #region Private Members
        private Conductor<T>.Collection.OneActive conductor;
        private PagedCollectionView collectionView;
        #endregion

        #region Public Properties (Bindable)
        public IObservableCollection<T> Items { get { return conductor.Items; } }
        public T ActiveItem { get { return conductor.ActiveItem; } }
        public string MoveNextCaption { get { return isLastView ? "Finish" : "Next"; } }
        public bool CanMovePrevious { get { return collectionView.CurrentPosition > 0; } }
        public bool CanMoveNext
        {
            get
            {
                var canMoveNext = collectionView.CurrentPosition < collectionView.ItemCount;
                var validatable = collectionView.CurrentItem as IValidatable;
                if (validatable != null)
                {
                    canMoveNext = canMoveNext && validatable.IsValid;
                }

                return canMoveNext;
            }
        }
        #endregion

        #region Constructors
        public Wizard()
        {
            conductor = new Conductor<T>.Collection.OneActive();
            conductor.ActivationProcessed +=
                (sender, e) =>
                {
                    ActivationProcessed(sender, e);
                };

            var activator = (conductor as IActivate);
            if (activator != null)
            {
                activator.Activate();
            }
        }
        #endregion

        #region Public Methods
        public void MoveNext()
        {
            if (isLastView)
            {
                Finish();
            }
            else
            {
                collectionView.MoveCurrentToNext();
                ActivateCurrentItem();
                Refresh();
            }
        }

        public void MovePrevious()
        {
            collectionView.MoveCurrentToPrevious();
            ActivateCurrentItem();
            Refresh();
        }

        public void Cancel()
        {
            TryClose();
        }
        #endregion

        #region Protected Methods
        protected void AddItems(params T[] items)
        {
            conductor.Items.AddRange(items);
            collectionView = new PagedCollectionView(conductor.Items, false, false);
        }

        protected void ActiveFirstItem()
        {
            collectionView.MoveCurrentToFirst();
            ActivateCurrentItem();
        }
        #endregion

        #region Virtual Methods
        protected virtual void Finish()
        {
            TryClose();
        }
        #endregion

        #region Private Properties & Methods
        private bool isLastView
        {
            get
            {
                return collectionView.CurrentPosition == collectionView.ItemCount - 1;
            }
        }

        private void ActivateCurrentItem()
        {
            conductor.ActivateItem(collectionView.CurrentItem as T);
            var notifyPropertyChanged = (collectionView.CurrentItem as INotifyPropertyChanged);
            if (notifyPropertyChanged != null)
            {
                notifyPropertyChanged.PropertyChanged -= notifyPropertyChanged_PropertyChanged;
                notifyPropertyChanged.PropertyChanged += notifyPropertyChanged_PropertyChanged;
            }
        }

        private void notifyPropertyChanged_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            Refresh();
        }
        #endregion
    }
}
